﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace bg
{
  public class ToeMove : GameMove
  {
    public int i { get; set; }

    public int j { get; set; }

    public int player { get; set; }

    public override string ToString()
    {
      return string.Format("Move player {0} to {1} {2}", player, i, j);
    }
  }

  public class Toe : GameState
  {
    public Toe(int w, int h)
    {
      this.w = w;
      this.h = h;
      player = 1;
      board = Enumerable.Repeat<int>(0, w * h).ToList();
    }

    public Toe(Toe toe, ToeMove move)
    {
      w = toe.w;
      h = toe.h;
      player = OtherPlayer(move.player);
      board = new List<int>(toe.board);
      Set(move.i, move.j, move.player);
    }

    public override string ToString()
    {
      string s = "";
      for (int j = 0; j < h; ++j)
      {
        for (int i = 0; i < w; ++i)
        {
          s += Get(i, j);
          if (i < w - 1)
          {
            s += "|";
          }
        }
        s += "\n";
        if (j < h - 1)
        {
          for (int i = 0; i < w - 1; ++i)
          {
            s += "-+";
          }
          s += "-\n";
        }
      }
      return s;
    }

    private void Set(int i, int j, int player)
    {
      board[j * w + i] = player;
    }

    private int Get(int i, int j)
    {
      return board[j * w + i];
    }

    private static int OtherPlayer(int player)
    {
      return player == 1 ? 2 : 1;
    }

    private int CountLines(int length, int count, int player)
    {
      int lines = 0;
      for (int i = 0; i < w; ++i)
      {
        for (int j = 0; j < h; ++j)
        {
          for (int di = -1; di <= 1; ++di)
          {
            for (int dj = -1; dj <= 1; ++dj)
            {
              int maxi = i + di * (length - 1);
              int maxj = j + dj * (length - 1);
              if ((di != 0 || dj != 0) && maxi >= 0 && maxi < w && maxj >= 0 && maxj < h)
              {
                int match = 0;
                int other = 0;
                for (int k = 0; k < length; ++k)
                {
                  int val = Get(i + k * di, j + k * dj);
                  if (val == player)
                  {
                    ++match;
                  }
                  else if (val != 0)
                  {
                    ++other;
                  }
                }
                if (other == 0 && match == count)
                {
                  ++lines;
                }
              }
            }
          }
        }
      }
      return lines;
    }

    private int w;

    private int h;

    private int player;

    private IList<int> board;

    #region GameState Members

    public IList<GameMove> Expand()
    {
      IList<GameMove> moves = new List<GameMove>();
      for (int i = 0; i < w; ++i)
      {
        for (int j = 0; j < h; ++j)
        {
          if (Get(i, j) == 0)
          {
            moves.Add(new ToeMove() { i = i, j = j, player = player });
          }
        }
      }
      return moves;
    }

    public GameState Apply(GameMove move)
    {
      return new Toe(this, move as ToeMove);
    }

    private int win { get { return w < h ? w : h; } }

    public float Eval(int player)
    {
      float score = 0;
      for (int p = 1; p <= 2; ++p)
      {
        for (int i = 1; i <= win; ++i)
        {
          int lines = CountLines(win, i, p);
          int mul = (int)Math.Pow(10, i - 1);
          int sign = (player == p ? 1 : -10);
          int dscore = lines * mul * sign;
          score += dscore;
        }
      }
      return score;
    }

    public int GetWinner()
    {
      for (int player = 1; player <= 2; ++player)
      {
        if (CountLines(win, win, player) > 0)
        {
          return player;
        }
      }
      if (board.All(val => val != 0))
      {
        return -1;
      }
      return 0;
    }

    public int GetPlayer()
    {
      return player;
    }

    #endregion
  }
}
